/**
 * Author:yuiop
 * Date:2022/3/17 17:46
 */
package com.devops.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.devops.admin.mapper.DOSClusterMapper;
import com.devops.admin.model.*;
import com.devops.admin.po.*;
import com.devops.admin.service.*;
import com.devops.admin.util.HttpCheckUtil;
import com.devops.admin.vo.DOSClusterVo;
import com.devops.admin.vo.DOSK8sIngressVo;
import com.devops.admin.vo.DOSPageVo;
import com.devops.admin.vo.DOSProjectVo;
import com.devops.shiro.vm.BaseUserVM;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class DOSClusterServiceImpl extends ServiceImpl<DOSClusterMapper, DOSCluster> implements DOSClusterService {

    @Autowired
    private DOSClusterNamespaceService dosClusterNamespaceService;
    @Autowired
    private DOSClusterInfoService dosClusterInfoService;
    @Autowired
    private DOSEnvClusterRelationService dosEnvClusterRelationService;
    @Autowired
    private DOSProjectEnvClusterNamespaceRelationService dosProjectEnvClusterNamespaceRelationService;
    @Autowired
    private DOSProjectOutService dosProjectOutService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Integer clusterId) {
        Assert.isTrue(null!=clusterId,"集群id不能为空");

        //首先查询集群是否绑定了project
        List<DOSProjectEnvClusterNamespaceRelation> dosProjects =
                dosProjectEnvClusterNamespaceRelationService.list(Wrappers.<DOSProjectEnvClusterNamespaceRelation>lambdaQuery()
                        .eq(DOSProjectEnvClusterNamespaceRelation::getClusterId,clusterId));
        Assert.isTrue(CollectionUtil.isEmpty(dosProjects),"集群绑定了项目不能删除");

        LambdaQueryWrapper<DOSClusterInfo> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(DOSClusterInfo::getClusterId,clusterId);
        dosClusterInfoService.remove(queryWrapper1);

        //删除集群下面的命名空间
        LambdaQueryWrapper<DOSClusterNamespace> namespaceQueryWrapper = new LambdaQueryWrapper<>();
        namespaceQueryWrapper.eq(DOSClusterNamespace::getClusterId,clusterId);
        dosClusterNamespaceService.remove(namespaceQueryWrapper);
        return baseMapper.deleteById(clusterId) > 0;
    }

    @Override
    public TableResult<DOSClusterPo> getList(DOSCluster cluster, DOSPageVo pageVo) {
        Page<DOSCluster> clusterPage = new Page<>(pageVo.getPageNum(), pageVo.getPageSize());
        TableResult<DOSClusterPo> tableResult = new TableResult<>();
        //按时间正序
        List<DOSClusterPo> clusterPos = baseMapper.getClusters(clusterPage, cluster);
        dealNamespace(clusterPos);
        Pagination p = Pagination.builder().pageNum(clusterPage.getCurrent()).pageSize(clusterPage.getSize()).total(clusterPage.getTotal()).build();
        tableResult.setList(clusterPos);
        tableResult.setPagination(p);
        return tableResult;
    }

    /**
     * @Author heyabo
     * @Description 组装namesapce
     * @Date 18:00 2022/3/28
     * @Param [dosClusterPage, pos]
     * @return void
     **/
    private void dealNamespace(List<DOSClusterPo> pos) {
        if (CollectionUtil.isNotEmpty(pos)) {
            List<Integer> clusterIds = pos.stream().map(DOSCluster::getId).collect(Collectors.toList());
            List<DOSClusterNamespace> dosClusterNamespaces = dosClusterNamespaceService.getByClusterId(clusterIds);
            Map<Integer, List<DOSClusterNamespace>> map = dosClusterNamespaces.stream().collect(Collectors.groupingBy(DOSClusterNamespace::getClusterId));
            pos.forEach(clusters-> clusters.setNamespaces(map.get(clusters.getId())));
        }
    }

    /**
     * env cluster namespace 三级级联返回数据
     * @return
     */
    @Override
    public List<DOSEnvClusterPo> getCascade() {
        //返回值初始化
        List<DOSEnvClusterPo> envClusterPos = new ArrayList<>();
        List<DOSProjectEnvClusterNamespaceRelationPo> cascade = baseMapper.getCascade();
        if (cascade.size() > 0) {
            //1-先按照envType分组
            Map<Integer,List<DOSProjectEnvClusterNamespaceRelationPo>> envMap =
                    cascade.stream().collect(Collectors.groupingBy(DOSProjectEnvClusterNamespaceRelationPo::getEnvType));
            envMap.forEach((k,v) -> {
                //这里的k是envType v里的每一个对象的envType相同 自然envTypeName也相同
                DOSEnvClusterPo dosEnvClusterPo = DOSEnvClusterPo.builder().envType(k).build();
                v.forEach(po -> {
                    //如果envTypeName未赋值 设置一下 v里的每一个对象的envType相同 自然envTypeName也相同
                    if (StrUtil.isBlank(dosEnvClusterPo.getEnvTypeName())) {
                        dosEnvClusterPo.setEnvTypeName(po.getEnvTypeName());
                    }
                    //初始化clusters
                    List<DOSClusterNamespacePo> clusters = new ArrayList<>();
                    //2-再按照clusterId分组
                    Map<Integer,List<DOSProjectEnvClusterNamespaceRelationPo>> clusterMap =
                            v.stream().collect(Collectors.groupingBy(DOSProjectEnvClusterNamespaceRelationPo::getClusterId));
                    clusterMap.forEach((k1,v1) -> {
                        //这里的k1是clusterId v1里的每一个对象的envType,envTypeName,clusterId,clusterName都相同
                        DOSClusterNamespacePo dosClusterNamespacePo = DOSClusterNamespacePo.builder().clusterId(k1).build();
                        //初始化namespaces
                        List<DOSClusterNamespace> namespaces = new ArrayList<>();
                        v1.forEach(po1 -> {
                            //如果clusterName未设置 这里设置一下 v1里的每一个对象的envType,envTypeName,clusterId,clusterName都相同
                            if (StrUtil.isBlank(dosClusterNamespacePo.getClusterName())) {
                                dosClusterNamespacePo.setClusterName(po1.getClusterName());
                            }
                            //build一个namespace
                            DOSClusterNamespace namespace =
                                    DOSClusterNamespace.builder().id(po1.getNamespaceId()).namespace(po1.getNamespace()).build();
                            //同一个cluster的namespace聚合一下
                            namespaces.add(namespace);
                        });
                        //设置cluster对应的namespaces
                        dosClusterNamespacePo.setNamespaces(namespaces);
                        //同一个env的cluster聚合一下
                        clusters.add(dosClusterNamespacePo);
                    });
                    //设置env的clusters
                    dosEnvClusterPo.setClusters(clusters);

                });
                //所有的env聚合一下 准备返回
                envClusterPos.add(dosEnvClusterPo);
            });
        }
        return envClusterPos;
    }

    @Override
    public String getHelmChartUrl(int projectId) {
        if (projectId>0){
            return baseMapper.getHelmChartUrl(projectId);
        }else {
            return dosClusterInfoService.getOneAvailableHelmWrapperAPIUrl();
        }
    }

    @Override
    public void checkCluster(DOSCluster cluster) {
        //HelmWrapperUrl格式校验
        Assert.isTrue(HttpCheckUtil.isURL(cluster.getHelmWrapperApiUrl()),"HelmWrapperUrl格式不合法");
        //HelmWrapperUrl是否可访问校验
        String result = "";
        try {
            result = HttpUtil.get(cluster.getHelmWrapperApiUrl(),6000);
        } catch (IORuntimeException e) {
            e.printStackTrace();
        } finally {
            Assert.isTrue(result.equals("Welcome helm wrapper server"),"HelmWrapperUrl无法访问");
        }

        //HelmWrapperUrl重复校验
        LambdaQueryWrapper<DOSCluster> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DOSCluster::getHelmWrapperApiUrl, cluster.getHelmWrapperApiUrl());
        List<DOSCluster> clusterList = baseMapper.selectList(queryWrapper);
        boolean matchFlag = false;
        //数据库中匹配到了数据
        if (clusterList.size() > 0) {
            //更新操作 进一步判断是否是更新数据本身
            if (cluster.getId() != null && cluster.getId() > 0) {
                for (DOSCluster tempCLuster:clusterList) {
                    //结果集中有一条数据 这条数据并不是要更新的这条数据 helmWrapperUrl重复实锤 跳出循环
                    if (tempCLuster.getId().intValue() != cluster.getId()) {
                        matchFlag = true;
                        break;
                    }
                }
            } else { //添加操作 helmWrapperUrl重复实锤
                matchFlag = true;
            }
        }
        Assert.isTrue(!matchFlag,"HelmWrapperUrl重复");

    }

    @Override
    public List<DOSClusterPo> getAllList(DOSCluster cluster) {
        List<DOSClusterPo> clusterPos = baseMapper.getClusters(cluster);
        List<Integer> clusterIds = clusterPos.stream().map(DOSClusterPo::getClusterId).collect(Collectors.toList());
        List<DOSClusterNamespace> namespaces = dosClusterNamespaceService.getByClusterId(clusterIds);
        Map<Integer, List<DOSClusterNamespace>> namespaceMap = namespaces.stream().collect(Collectors.groupingBy(DOSClusterNamespace::getClusterId));
        clusterPos.forEach( clusters -> {
            if (CollectionUtil.isNotEmpty(namespaceMap.get(clusters.getId()))){
                clusters.setNamespaces(namespaceMap.get(clusters.getId()));
            }
        });
        return clusterPos;
    }

    @Override
    public List<DOSClusterNamespacePo> queryClusterNamespace() {
        BaseUserVM user = (BaseUserVM) SecurityUtils.getSubject().getPrincipal();
        List<DOSClusterNamespacePo> clusters;
        // 按照登录人员的角色信息查询集群命名空间级联
        if (user.getAdminRole()) {
            clusters = baseMapper.getClusterNamespaces(null);
        }else {
            List<DOSProjectPo> projectsByUserId = dosProjectOutService.getProjectsByUserId(DOSProjectVo.builder()
                    .userId(user.getId()).build());
            List<Integer> projectIds = projectsByUserId.stream().map(DOSProjectPo::getId).collect(Collectors.toList());
            clusters = baseMapper.getClusterNamespacesByProjectIds(projectIds);
        }
        // 如果查询结果为空直接返回
        if (CollectionUtil.isEmpty(clusters)){
            return null;
        }
        Map<Integer, List<DOSClusterNamespacePo>> clusterMap = clusters.stream().collect(Collectors.groupingBy(DOSClusterNamespacePo::getClusterId));
        List<DOSClusterNamespacePo> clusterNamespaces = new ArrayList<>();
        // 循环获取集群命名空间级联信息
        for (Map.Entry<Integer, List<DOSClusterNamespacePo>> clusterList : clusterMap.entrySet()) {
            List<DOSClusterNamespace> namespaces = new ArrayList<>();
            DOSClusterNamespacePo clusterNamespacePo = clusterList.getValue().get(0);
            for (DOSClusterNamespacePo namespacePo : clusterList.getValue()) {
                DOSClusterNamespace namespace = DOSClusterNamespace.builder()
                        .id(namespacePo.getNamespaceId())
                        .namespace(namespacePo.getNamespace())
                        .build();
                namespaces.add(namespace);
            }
            clusterNamespacePo.setNamespaces(namespaces);
            clusterNamespaces.add(clusterNamespacePo);
        }
        return clusterNamespaces;
    }

    @Override
    public TableResult<DOSClusterPo> saveCluster(DOSClusterVo clusterVo) {
        Assert.isTrue(clusterVo.getEnvType() != null && clusterVo.getEnvType() > 0,"环境类型不能为空");
        Assert.isTrue(StrUtil.isNotBlank(clusterVo.getHelmWrapperApiUrl()),"helmWrapperApiUrl不能为空");
        Assert.isTrue(StrUtil.isNotBlank(clusterVo.getClusterLogProjectName()),"集群项目名称不能为空");
        DOSCluster cluster = new DOSCluster();
        BeanUtil.copyProperties(clusterVo,cluster);
        //集群名称和helm名称不能重复
        this.checkCluster(clusterVo);
        this.save(cluster);
        //保存clusterInfo
        DOSClusterInfo info = new DOSClusterInfo();
        info.setClusterId(cluster.getId());
        info.setConfigPath(clusterVo.getConfigPath());
        info.setHelmWrapperApiUrl(clusterVo.getHelmWrapperApiUrl());
        if (StrUtil.isNotBlank(clusterVo.getConfigContent())){
            info.setConfigContent(clusterVo.getConfigContent());
        }
        info.setClusterLogProjectName(clusterVo.getClusterLogProjectName());
        dosClusterInfoService.save(info);
        //保存集群与环境类型的关系
        DOSEnvClusterRelation relation = new DOSEnvClusterRelation();
        relation.setClusterId(cluster.getId());
        relation.setEnvType(clusterVo.getEnvType());
        dosEnvClusterRelationService.save(relation);
        TableResult<DOSClusterPo> list = this.getList(cluster,new DOSPageVo());

        return list;
    }

    @Override
    public Boolean updateCluster(DOSClusterVo clusterVo) {
        Assert.isTrue(null!=clusterVo.getId(),"集群id不能为空");
        Assert.isTrue(clusterVo.getEnvType() != null && clusterVo.getEnvType() > 0,"环境类型不能为空");
        Assert.isTrue(StrUtil.isNotBlank(clusterVo.getHelmWrapperApiUrl()),"helmWrapperApiUrl不能为空");
        Assert.isTrue(StrUtil.isNotBlank(clusterVo.getClusterLogProjectName()),"集群项目名称不能为空");
        DOSCluster cluster = new DOSCluster();
        BeanUtil.copyProperties(clusterVo,cluster);
        //集群名称和helm名称不能重复
        this.checkCluster(cluster);
        //获取clusterInfo
        LambdaQueryWrapper<DOSClusterInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DOSClusterInfo::getClusterId,cluster.getId());
        if (clusterVo.getClusterInfoId()!=null && clusterVo.getClusterInfoId()>0){
            queryWrapper.eq(DOSClusterInfo::getId,clusterVo.getClusterInfoId());
        }
        DOSClusterInfo clusterInfo = dosClusterInfoService.getOne(queryWrapper);
        //判断是否已经存clusterInfo
        if (null!=clusterInfo) {
            clusterInfo.setHelmWrapperApiUrl(clusterVo.getHelmWrapperApiUrl());
            clusterInfo.setConfigPath(clusterVo.getConfigPath());
            clusterInfo.setClusterId(clusterVo.getId());
            clusterInfo.setClusterLogProjectName(clusterVo.getClusterLogProjectName());
            if (StrUtil.isNotBlank(clusterVo.getConfigContent())) {
                clusterInfo.setConfigContent(clusterVo.getConfigContent());
            }
            dosClusterInfoService.updateById(clusterInfo);
        } else {
            clusterInfo = new DOSClusterInfo();
            clusterInfo.setHelmWrapperApiUrl(clusterVo.getHelmWrapperApiUrl());
            clusterInfo.setConfigPath(clusterVo.getConfigPath());
            clusterInfo.setClusterId(clusterVo.getId());
            clusterInfo.setClusterLogProjectName(clusterVo.getClusterLogProjectName());
            if (StrUtil.isNotBlank(clusterVo.getConfigContent())) {
                clusterInfo.setConfigContent(clusterVo.getConfigContent());
            }
            dosClusterInfoService.save(clusterInfo);
        }
        // 查询集群与环境的关系
        DOSEnvClusterRelation relation = dosEnvClusterRelationService.getOne(Wrappers.<DOSEnvClusterRelation>lambdaQuery()
                .eq(DOSEnvClusterRelation::getEnvType,clusterVo.getEnvType())
                .eq(DOSEnvClusterRelation::getClusterId,clusterVo.getId()));
        if (relation != null){
            relation.setClusterId(cluster.getId());
            relation.setEnvType(clusterVo.getEnvType());
            dosEnvClusterRelationService.updateById(relation);
        } else {
            relation = new DOSEnvClusterRelation();
            relation.setClusterId(cluster.getId());
            relation.setEnvType(clusterVo.getEnvType());
            dosEnvClusterRelationService.save(relation);
        }
        return this.updateById(cluster);
    }

    @Override
    public List<DOSClusterPo> getClusterAndNamespaceAndClusterInfo(DOSK8sIngressVo ingressVo) {

        return baseMapper.getClusterAndNamespaceAndClusterInfo(ingressVo);
    }

    @Override
    public List<DOSClusterPo> getClusterAndNamespaceAndClusterInfo(DOSK8sIngressVo ingressVo, List<Integer> projectIds) {
        return baseMapper.getClusterAndNamespaceAndClusterInfos(ingressVo,projectIds);
    }
}
